<?php

namespace liketp\model\concern;

use Closure;
use liketp\exception\Exception;

trait DbAction
{
    public static function create(array $data)
    {
        $model = new static();
        $model->save($data);
        return $model;
    }

    public function save(?array $data = null)
    {
        if ($data) {
            $this->data($data);
        }
        if ($this->isExists || isset($data[$this->pk])) {
            if ($this->isExists == false) {
                $model = $this->find($data[$this->pk]);
                unset($data[$this->pk]);
                $res = $model->data($data)->updateData();
            } else {

                $res = $this->updateData();
            }
        } else {
            $res = $this->insertData();
        }
        return $res;
    }

    public static function saveAll(array $data)
    {
        foreach ($data as $item) {
            $model = new static();
            $model->save($item);
        }
        return true;
    }

    /**
     * destroy(1)
     * destroy([1,2,3])
     * destroy([id=>1])
     * destroy(function(){})
     */
    public static function destroy($data)
    {
        $model = new static();
        $query = $model->db();
        $pk = $model->pk();

        // destroy(1)
        if (is_numeric($data)) {
            $msg = 'model has no primary key, [' . get_class($model) . ']';
            if (empty($pk)) throw new Exception($msg);
            $query->where($pk, $data);
        }

        // destroy([...])
        if (is_array($data) && !empty($data)) {

            if (is_numeric(key($data))) {
                // destroy([[id, '>', 1], [name, 'like', '%test%']])
                if (is_array($data[0])) {
                    $query->where($data);
                }
                // destroy([1,2,3])    
                else {
                    $pk && $query->whereIn($pk, $data);
                }
            }
            // destroy([id=>1])
            else {
                $query->where($data);
            }
        }

        // destroy(function($query){})
        if ($data instanceof Closure) {
            $data($query);
        }

        $res = $query->delete();
        return $res;
    }

    public function delete(): bool
    {
        $where = $this->getWhere();

        if (!empty($where)) {
            $query = $this->db();
            $query->where($where)->delete();

            $this->isExists = false;
            return true;
        } else {
            return false;
        }
    }


    //------------------------------------------------ protected
    protected function getWhere(): array
    {
        $where = [];
        if ($this->pk && isset($this->origin[$this->pk])) {
            $where[] = [$this->pk, '=', $this->origin[$this->pk]];
        }

        return $where;
    }

    protected function insertData(): bool
    {
        $data = $this->data();
        $query = $this->db();
        $id = $query->insertGetId($data);

        if (!$id) return false;

        if (is_null($this->getData($this->pk))) {
            $this->setData($this->pk, $id);
        }
        $this->copyDataToOrigin();
        $this->isExists(true);

        return true;
    }

    protected function updateData(): bool
    {
        $data = $this->data();
        $query = $this->db();
        $changedData = $this->getChangedData($data, $this->origin);
        $where = $this->getWhere();

        if (!empty($changedData) && !empty($where)) {
            $res = $query->where($where)->update($changedData);
            if ($res) {
                $this->copyDataToOrigin();
                return true;
            } else {
                return false;
            }
        } else {
            return true;
        }
    }

    protected function getChangedData($data, $origin): array
    {
        $addFields = array_diff_key($data, $origin);
        $updateFields = array_diff_assoc($data, $origin);
        return array_merge($addFields, $updateFields);
    }
}
